/*
 * @Author: xiaosihan
 * @Date: 2025-10-26 10:00:00
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2025-11-03 17:47:55
 */
import express from "express";
import database from "../database";
import ServiceBase from "./ServiceBase";
import OSS from "ali-oss";
import fs from "fs";
import path from "path";
import axios from "axios";
import { v4 as uuidv4 } from 'uuid';
import crypto from 'crypto';

// 配置OSS客户端
const oss = new OSS({
    region: 'oss-cn-shanghai',
    accessKeyId: process.env.accessKeyId || "",
    accessKeySecret: process.env.accessKeySecret || "",
    bucket: 'www-3dkeshihua',
});

// Autodesk Forge 配置
const FORGE_CLIENT_ID = process.env.FORGE_CLIENT_ID || "your_client_id";
const FORGE_CLIENT_SECRET = process.env.FORGE_CLIENT_SECRET || "your_client_secret";
const FORGE_AUTH_URL = "https://developer.api.autodesk.com/authentication/v1/authenticate";
const FORGE_JOB_URL = "https://developer.api.autodesk.com/modelderivative/v2/designdata/job";

// CAD转换服务
export default class CadConversionService extends ServiceBase {

    constructor(req: express.Request = {} as express.Request, res: express.Response = {} as express.Response) {
        super(req, res);
    }

    // 开始转换任务处理器
    async start_conversion_processor() {
        while (true) {
            // 获取待处理的转换任务
            const pendingTask = await database.cadConversion.findOne({
                where: {
                    status: 'pending'
                },
                order: [['createdAt', 'ASC']]
            });

            if (pendingTask) {
                // 处理转换任务
                await this.process_conversion_task(pendingTask);
            }

            // 等待一段时间后再次检查
            await new Promise(resolve => setTimeout(resolve, 5000));
        }
    }

    // 处理转换任务
    async process_conversion_task(task: any) {
        // 更新任务状态为处理中
        await task.update({
            status: 'processing',
            progress: 10
        });

        // 下载原始DWG文件到本地
        const localFilePath = await this.download_file_from_oss(task.originalFileUrl);

        // 更新进度
        await task.update({ progress: 30 });

        // 执行真实的转换过程
        const convertedFilePath = await this.real_conversion(localFilePath, task.targetFormat);

        // 更新进度
        await task.update({ progress: 80 });

        // 生成转换后的文件名
        const convertedFileName = `${path.basename(task.originalFileName, path.extname(task.originalFileName))}.${task.targetFormat}`;

        // 上传转换后的文件到OSS
        const ossPath = `cad_conversions/${Date.now()}-${convertedFileName}`;
        const result = await oss.put(ossPath, convertedFilePath);

        // 更新任务状态为完成
        await task.update({
            status: 'completed',
            progress: 100,
            convertedFileName,
            convertedFileUrl: result.url,
            conversionTime: Math.floor((Date.now() - new Date(task.createdAt).getTime()) / 1000) // 转换用时（秒）
        });

        // 清理临时文件
        this.cleanup_temp_files(localFilePath, convertedFilePath);
    }

    // 从OSS下载文件
    async download_file_from_oss(fileUrl: string): Promise<string> {
        const ossPath = fileUrl.replace('http://www-3dkeshihua.oss-cn-shanghai.aliyuncs.com/', '');
        const localFilePath = path.join(__dirname, `../../uploads/${uuidv4()}.dwg`);

        // 确保uploads目录存在
        const uploadDir = path.join(__dirname, '../../uploads');
        if (!fs.existsSync(uploadDir)) {
            fs.mkdirSync(uploadDir, { recursive: true });
        }

        // 下载文件
        await oss.get(ossPath, localFilePath);
        return localFilePath;
    }

    // 获取Autodesk Forge访问令牌
    async get_forge_access_token(): Promise<string> {
        const response = await axios.post(FORGE_AUTH_URL,
            `client_id=${FORGE_CLIENT_ID}&client_secret=${FORGE_CLIENT_SECRET}&grant_type=client_credentials&scope=data:read data:write data:create bucket:read bucket:create`,
            {
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            }
        );
        return response.data.access_token;
    }

    // 将本地文件上传到Forge并获取URN
    async upload_to_forge(filePath: string): Promise<string> {
        const accessToken = await this.get_forge_access_token();
        const fileName = path.basename(filePath);

        // 步骤1: 创建一个bucket（如果不存在）
        const bucketKey = `cad-conversion-bucket-${Date.now()}`;
        // 注意：这里不使用try/catch，允许错误抛出以便上层处理
        await axios.post(
            `https://developer.api.autodesk.com/oss/v2/buckets`,
            { bucketKey, policyKey: 'transient' },
            { headers: { 'Authorization': `Bearer ${accessToken}` } }
        );

        // 步骤2: 上传文件到bucket
        const fileStream = fs.createReadStream(filePath);
        const fileStats = fs.statSync(filePath);

        await axios.put(
            `https://developer.api.autodesk.com/oss/v2/buckets/${bucketKey}/objects/${fileName}`,
            fileStream,
            {
                headers: {
                    'Authorization': `Bearer ${accessToken}`,
                    'Content-Type': 'application/octet-stream',
                    'Content-Length': fileStats.size
                }
            }
        );

        // 步骤3: 获取文件URN并进行base64编码
        const urn = Buffer.from(`${bucketKey}/${fileName}`).toString('base64').replace(/=/g, '');
        return urn;
    }

    // 使用Autodesk Forge API进行DWG文件转换
    async convert_using_forge(sourcePath: string, targetFormat: string): Promise<string> {
        // 上传文件到Forge并获取URN
        const urn = await this.upload_to_forge(sourcePath);
        const accessToken = await this.get_forge_access_token();

        // 提交转换作业
        const response = await axios.post(
            FORGE_JOB_URL,
            {
                input: { urn: urn },
                output: {
                    formats: [{
                        type: targetFormat.toLowerCase() === 'glb' ? 'glTF' : targetFormat,
                        views: ['3d']
                    }]
                }
            },
            {
                headers: {
                    'Authorization': `Bearer ${accessToken}`,
                    'Content-Type': 'application/json'
                }
            }
        );

        // 轮询检查转换状态
        let conversionStatus = 'inprogress';
        let resultUrn = '';

        while (conversionStatus === 'inprogress' || conversionStatus === 'pending') {
            await new Promise(resolve => setTimeout(resolve, 10000)); // 每10秒检查一次

            const statusResponse = await axios.get(
                `https://developer.api.autodesk.com/modelderivative/v2/designdata/${urn}/manifest`,
                {
                    headers: {
                        'Authorization': `Bearer ${accessToken}`
                    }
                }
            );

            conversionStatus = statusResponse.data.status;

            // 检查是否有转换成功的文件
            if (conversionStatus === 'success' && statusResponse.data.derivatives) {
                for (const derivative of statusResponse.data.derivatives) {
                    if (derivative.outputType === (targetFormat.toLowerCase() === 'glb' ? 'glTF' : targetFormat)) {
                        resultUrn = derivative.urn;
                        break;
                    }
                }
                break;
            }
        }

        if (conversionStatus !== 'success') {
            throw new Error(`转换失败，状态: ${conversionStatus}`);
        }

        // 下载转换后的文件
        const resultResponse = await axios.get(
            `https://developer.api.autodesk.com/modelderivative/v2/designdata/${urn}/manifest/${resultUrn}`,
            {
                headers: {
                    'Authorization': `Bearer ${accessToken}`
                },
                responseType: 'arraybuffer'
            }
        );

        // 保存转换后的文件到本地
        const convertedFilePath = path.join(__dirname, `../../uploads/${uuidv4()}.${targetFormat}`);
        fs.writeFileSync(convertedFilePath, Buffer.from(resultResponse.data));

        return convertedFilePath;
    }

    // 实际的DWG转换实现
    async real_conversion(sourcePath: string, targetFormat: string): Promise<string> {
        // 这里实现真实的DWG转换逻辑
        // 默认使用Autodesk Forge API进行转换
        console.log(`开始转换文件: ${sourcePath} -> ${targetFormat}`);
        const convertedFilePath = await this.convert_using_forge(sourcePath, targetFormat);
        console.log(`文件转换成功: ${sourcePath} -> ${convertedFilePath}`);
        return convertedFilePath;
    }

    // 清理临时文件
    cleanup_temp_files(...filePaths: string[]): void {
        filePaths.forEach(filePath => {
            if (fs.existsSync(filePath)) {
                fs.unlinkSync(filePath);
            }
        });
    }

    // 创建转换任务
    write_conversion_task = async (params: Record<string, any>) => {
        // 参数校验
        this.verifyParams(['fileUrl', 'fileName', 'fileSize']);

        const { fileUrl, fileName, fileSize, targetFormat = 'glb' } = params;

        if (!['fbx', 'glb'].includes(targetFormat)) {
            throw new Error('不支持的目标格式，仅支持fbx和glb');
        }

        // 创建转换任务记录
        const conversionTask = await database.cadConversion.create({
            originalFileName: fileName,
            originalFileUrl: fileUrl,
            originalFileSize: fileSize,
            targetFormat
        });

        return conversionTask.dataValues;
    };

    // 获取转换任务状态
    get_conversion_status = async (params: Record<string, any>) => {
        // 参数校验
        this.verifyParams(['id']);

        const { id } = params;

        if (isNaN(parseInt(id))) {
            throw new Error('请提供有效的任务ID');
        }

        const task = await database.cadConversion.findByPk(parseInt(id));

        if (!task) {
            throw new Error('未找到指定的转换任务');
        }

        return task.dataValues;
    };

    // 获取转换任务列表
    get_conversion_tasks = async (params: Record<string, any>) => {
        // 参数校验
        this.verifyParams(['page', 'pageSize']);

        const page = parseInt(params.page as string) || 1;
        const pageSize = parseInt(params.pageSize as string) || 10;
        const offset = (page - 1) * pageSize;

        // 构建查询条件
        const whereCondition: any = {};
        if (params.status) {
            whereCondition.status = params.status;
        }

        // 查询数据并计算总数
        const [data, total] = await Promise.all([
            database.cadConversion.findAll({
                where: whereCondition,
                offset,
                limit: pageSize,
                order: [['createdAt', 'DESC']]
            }),
            database.cadConversion.count({ where: whereCondition })
        ]);

        return {
            data: data.map(item => item.dataValues),
            total,
            page,
            pageSize
        };
    };
}